import java.util.*;

/**
 * Complete Design Patterns Examples
 * This class demonstrates various design patterns in Java with comprehensive examples
 */
public class DesignPatternsComplete {
    public static void main(String[] args) {
        System.out.println("=== Java Design Patterns Examples ===\n");
        
        // 13.1 Creational Patterns
        demonstrateCreationalPatterns();
        
        // 13.2 Structural Patterns
        demonstrateStructuralPatterns();
        
        // 13.3 Behavioral Patterns
        demonstrateBehavioralPatterns();
    }
    
    // 13.1 Creational Patterns
    public static void demonstrateCreationalPatterns() {
        System.out.println("=== 13.1 Creational Patterns ===");
        
        // Singleton Pattern
        demonstrateSingletonPattern();
        
        // Factory Pattern
        demonstrateFactoryPattern();
        
        // Abstract Factory Pattern
        demonstrateAbstractFactoryPattern();
        
        // Builder Pattern
        demonstrateBuilderPattern();
        
        // Prototype Pattern
        demonstratePrototypePattern();
        
        System.out.println();
    }
    
    // 13.2 Structural Patterns
    public static void demonstrateStructuralPatterns() {
        System.out.println("=== 13.2 Structural Patterns ===");
        
        // Adapter Pattern
        demonstrateAdapterPattern();
        
        // Decorator Pattern
        demonstrateDecoratorPattern();
        
        // Proxy Pattern
        demonstrateProxyPattern();
        
        // Facade Pattern
        demonstrateFacadePattern();
        
        // Bridge Pattern
        demonstrateBridgePattern();
        
        // Composite Pattern
        demonstrateCompositePattern();
        
        // Flyweight Pattern
        demonstrateFlyweightPattern();
        
        System.out.println();
    }
    
    // 13.3 Behavioral Patterns
    public static void demonstrateBehavioralPatterns() {
        System.out.println("=== 13.3 Behavioral Patterns ===");
        
        // Strategy Pattern
        demonstrateStrategyPattern();
        
        // Observer Pattern
        demonstrateObserverPattern();
        
        // Command Pattern
        demonstrateCommandPattern();
        
        // Template Method Pattern
        demonstrateTemplateMethodPattern();
        
        // Iterator Pattern
        demonstrateIteratorPattern();
        
        // Chain of Responsibility Pattern
        demonstrateChainOfResponsibilityPattern();
        
        // State Pattern
        demonstrateStatePattern();
        
        // Visitor Pattern
        demonstrateVisitorPattern();
        
        // Mediator Pattern
        demonstrateMediatorPattern();
        
        // Memento Pattern
        demonstrateMementoPattern();
        
        // Interpreter Pattern
        demonstrateInterpreterPattern();
        
        System.out.println();
    }
    
    // Singleton Pattern
    public static void demonstrateSingletonPattern() {
        System.out.println("1. Singleton Pattern:");
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();
        System.out.println("  singleton1 == singleton2: " + (singleton1 == singleton2));
        System.out.println("  singleton1.hashCode(): " + singleton1.hashCode());
        System.out.println("  singleton2.hashCode(): " + singleton2.hashCode());
    }
    
    // Factory Pattern
    public static void demonstrateFactoryPattern() {
        System.out.println("2. Factory Pattern:");
        ShapeFactory factory = new ShapeFactory();
        Shape shape1 = factory.getShape("CIRCLE");
        Shape shape2 = factory.getShape("RECTANGLE");
        shape1.draw();
        shape2.draw();
    }
    
    // Abstract Factory Pattern
    public static void demonstrateAbstractFactoryPattern() {
        System.out.println("3. Abstract Factory Pattern:");
        AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
        Shape shape1 = shapeFactory.getShape("CIRCLE");
        shape1.draw();
        
        AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");
        Color color1 = colorFactory.getColor("RED");
        color1.fill();
    }
    
    // Builder Pattern
    public static void demonstrateBuilderPattern() {
        System.out.println("4. Builder Pattern:");
        MealBuilder mealBuilder = new MealBuilder();
        Meal vegMeal = mealBuilder.prepareVegMeal();
        System.out.println("Veg Meal");
        vegMeal.showItems();
        System.out.println("Total Cost: " + vegMeal.getCost());
        
        Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
        System.out.println("\nNon-Veg Meal");
        nonVegMeal.showItems();
        System.out.println("Total Cost: " + nonVegMeal.getCost());
    }
    
    // Prototype Pattern
    public static void demonstratePrototypePattern() {
        System.out.println("5. Prototype Pattern:");
        ShapeCache.loadCache();
        
        Shape clonedShape1 = ShapeCache.getShape("1");
        System.out.println("Shape : " + clonedShape1.getType());
        
        Shape clonedShape2 = ShapeCache.getShape("2");
        System.out.println("Shape : " + clonedShape2.getType());
        
        Shape clonedShape3 = ShapeCache.getShape("3");
        System.out.println("Shape : " + clonedShape3.getType());
    }
    
    // Adapter Pattern
    public static void demonstrateAdapterPattern() {
        System.out.println("1. Adapter Pattern:");
        AudioPlayer audioPlayer = new AudioPlayer();
        audioPlayer.play("mp3", "beyond the horizon.mp3");
        audioPlayer.play("mp4", "alone.mp4");
        audioPlayer.play("vlc", "far far away.vlc");
        audioPlayer.play("avi", "mind me.avi");
    }
    
    // Decorator Pattern
    public static void demonstrateDecoratorPattern() {
        System.out.println("2. Decorator Pattern:");
        Shape circle = new Circle();
        Shape redCircle = new RedShapeDecorator(new Circle());
        Shape redRectangle = new RedShapeDecorator(new Rectangle());
        
        System.out.println("Circle with normal border");
        circle.draw();
        
        System.out.println("\nCircle of red border");
        redCircle.draw();
        
        System.out.println("\nRectangle of red border");
        redRectangle.draw();
    }
    
    // Proxy Pattern
    public static void demonstrateProxyPattern() {
        System.out.println("3. Proxy Pattern:");
        Image image = new ProxyImage("test_10mb.jpg");
        
        // Image will be loaded from disk
        image.display();
        System.out.println("");
        
        // Image will not be loaded from disk
        image.display();
    }
    
    // Facade Pattern
    public static void demonstrateFacadePattern() {
        System.out.println("4. Facade Pattern:");
        ShapeMaker shapeMaker = new ShapeMaker();
        shapeMaker.drawCircle();
        shapeMaker.drawRectangle();
        shapeMaker.drawSquare();
    }
    
    // Bridge Pattern
    public static void demonstrateBridgePattern() {
        System.out.println("5. Bridge Pattern:");
        Shape1 bridgeCircle1 = new Circle1(100, 100, 10, new RedCircle());
        Shape1 bridgeCircle2 = new Circle1(100, 100, 10, new GreenCircle());
        
        bridgeCircle1.draw();
        bridgeCircle2.draw();
    }
    
    // Composite Pattern
    public static void demonstrateCompositePattern() {
        System.out.println("6. Composite Pattern:");
        Employee CEO = new Employee("John", "CEO", 30000);
        
        Employee headSales = new Employee("Robert", "Head Sales", 20000);
        Employee headMarketing = new Employee("Michel", "Head Marketing", 20000);
        
        Employee clerk1 = new Employee("Laura", "Marketing", 10000);
        Employee clerk2 = new Employee("Bob", "Marketing", 10000);
        
        Employee salesExecutive1 = new Employee("Richard", "Sales", 10000);
        Employee salesExecutive2 = new Employee("Rob", "Sales", 10000);
        
        CEO.add(headSales);
        CEO.add(headMarketing);
        
        headSales.add(salesExecutive1);
        headSales.add(salesExecutive2);
        
        headMarketing.add(clerk1);
        headMarketing.add(clerk2);
        
        // Print all employees
        System.out.println(CEO);
        for (Employee headEmployee : CEO.getSubordinates()) {
            System.out.println(headEmployee);
            for (Employee employee : headEmployee.getSubordinates()) {
                System.out.println(employee);
            }
        }
    }
    
    // Flyweight Pattern
    public static void demonstrateFlyweightPattern() {
        System.out.println("7. Flyweight Pattern:");
        String[] colors = {"Red", "Green", "Blue", "White", "Black"};
        for (int i = 0; i < 20; ++i) {
            Circle1 circle = 
                (Circle1) ShapeFactory1.getCircle(
                    colors[(int) (Math.random() * colors.length)]
                );
            circle.setX((int) (Math.random() * 100));
            circle.setY((int) (Math.random() * 100));
            circle.setRadius(100);
            circle.draw();
        }
    }
    
    // Strategy Pattern
    public static void demonstrateStrategyPattern() {
        System.out.println("1. Strategy Pattern:");
        Context context = new Context(new OperationAdd());
        System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
        
        context = new Context(new OperationSubstract());
        System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
        
        context = new Context(new OperationMultiply());
        System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
    }
    
    // Observer Pattern
    public static void demonstrateObserverPattern() {
        System.out.println("2. Observer Pattern:");
        Subject subject = new Subject();
        
        new HexaObserver(subject);
        new OctalObserver(subject);
        new BinaryObserver(subject);
        
        System.out.println("First state change: 15");
        subject.setState(15);
        System.out.println("Second state change: 10");
        subject.setState(10);
    }
    
    // Command Pattern
    public static void demonstrateCommandPattern() {
        System.out.println("3. Command Pattern:");
        Stock abcStock = new Stock();
        
        BuyStock buyStockOrder = new BuyStock(abcStock);
        SellStock sellStockOrder = new SellStock(abcStock);
        
        Broker broker = new Broker();
        broker.takeOrder(buyStockOrder);
        broker.takeOrder(sellStockOrder);
        
        broker.placeOrders();
    }
    
    // Template Method Pattern
    public static void demonstrateTemplateMethodPattern() {
        System.out.println("4. Template Method Pattern:");
        Game game = new Cricket();
        game.play();
        System.out.println();
        game = new Football();
        game.play();
    }
    
    // Iterator Pattern
    public static void demonstrateIteratorPattern() {
        System.out.println("5. Iterator Pattern:");
        NameRepository namesRepository = new NameRepository();
        
        for (Iterator iter = namesRepository.getIterator(); iter.hasNext();) {
            String name = (String) iter.next();
            System.out.println("Name : " + name);
        }
    }
    
    // Chain of Responsibility Pattern
    public static void demonstrateChainOfResponsibilityPattern() {
        System.out.println("6. Chain of Responsibility Pattern:");
        AbstractLogger loggerChain = getChainOfLoggers();
        
        loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");
        loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information.");
        loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information.");
    }
    
    private static AbstractLogger getChainOfLoggers() {
        AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
        AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
        AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
        
        errorLogger.setNextLogger(fileLogger);
        fileLogger.setNextLogger(consoleLogger);
        
        return errorLogger;
    }
    
    // State Pattern
    public static void demonstrateStatePattern() {
        System.out.println("7. State Pattern:");
        Context1 context = new Context1();
        
        StartState startState = new StartState();
        startState.doAction(context);
        
        System.out.println(context.getState().toString());
        
        StopState stopState = new StopState();
        stopState.doAction(context);
        
        System.out.println(context.getState().toString());
    }
    
    // Visitor Pattern
    public static void demonstrateVisitorPattern() {
        System.out.println("8. Visitor Pattern:");
        ComputerPart computer = new Computer();
        computer.accept(new ComputerPartDisplayVisitor());
    }
    
    // Mediator Pattern
    public static void demonstrateMediatorPattern() {
        System.out.println("9. Mediator Pattern:");
        User robert = new User("Robert");
        User john = new User("John");
        
        robert.sendMessage("Hi! John!");
        john.sendMessage("Hello! Robert!");
    }
    
    // Memento Pattern
    public static void demonstrateMementoPattern() {
        System.out.println("10. Memento Pattern:");
        Originator originator = new Originator();
        CareTaker careTaker = new CareTaker();
        
        originator.setState("State #1");
        originator.setState("State #2");
        careTaker.add(originator.saveStateToMemento());
        
        originator.setState("State #3");
        careTaker.add(originator.saveStateToMemento());
        
        originator.setState("State #4");
        System.out.println("Current State: " + originator.getState());
        
        originator.getStateFromMemento(careTaker.get(0));
        System.out.println("First saved State: " + originator.getState());
        originator.getStateFromMemento(careTaker.get(1));
        System.out.println("Second saved State: " + originator.getState());
    }
    
    // Interpreter Pattern
    public static void demonstrateInterpreterPattern() {
        System.out.println("11. Interpreter Pattern:");
        Expression person1 = new TerminalExpression("Kushagra");
        Expression person2 = new TerminalExpression("Lokesh");
        Expression isSingle = new OrExpression(person1, person2);
        
        Expression vikram = new TerminalExpression("Vikram");
        Expression committed = new TerminalExpression("Committed");
        Expression isCommitted = new AndExpression(vikram, committed);
        
        System.out.println("Kushagra is single? " + isSingle.interpret("Kushagra"));
        System.out.println("Vikram is committed? " + isCommitted.interpret("Committed, Vikram"));
    }
}

// Creational Patterns Implementations

// Singleton Pattern
class Singleton {
    private static Singleton instance = new Singleton();
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        return instance;
    }
    
    public void showMessage() {
        System.out.println("Hello World!");
    }
}

// Factory Pattern
interface Shape {
    void draw();
}

class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Rectangle::draw() method.");
    }
}

class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Square::draw() method.");
    }
}

class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Circle::draw() method.");
    }
}

class ShapeFactory {
    public Shape getShape(String shapeType) {
        if (shapeType == null) {
            return null;
        }
        if (shapeType.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
            return new Rectangle();
        } else if (shapeType.equalsIgnoreCase("SQUARE")) {
            return new Square();
        }
        return null;
    }
}

// Abstract Factory Pattern
interface Color {
    void fill();
}

class Red implements Color {
    @Override
    public void fill() {
        System.out.println("Inside Red::fill() method.");
    }
}

class Green implements Color {
    @Override
    public void fill() {
        System.out.println("Inside Green::fill() method.");
    }
}

class Blue implements Color {
    @Override
    public void fill() {
        System.out.println("Inside Blue::fill() method.");
    }
}

abstract class AbstractFactory {
    abstract Color getColor(String color);
    abstract Shape getShape(String shape);
}

class ShapeFactory1 extends AbstractFactory {
    @Override
    public Shape getShape(String shapeType) {
        if (shapeType == null) {
            return null;
        }
        if (shapeType.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
            return new Rectangle();
        } else if (shapeType.equalsIgnoreCase("SQUARE")) {
            return new Square();
        }
        return null;
    }
    
    @Override
    Color getColor(String color) {
        return null;
    }
}

class ColorFactory extends AbstractFactory {
    @Override
    public Shape getShape(String shapeType) {
        return null;
    }
    
    @Override
    Color getColor(String color) {
        if (color == null) {
            return null;
        }
        if (color.equalsIgnoreCase("RED")) {
            return new Red();
        } else if (color.equalsIgnoreCase("GREEN")) {
            return new Green();
        } else if (color.equalsIgnoreCase("BLUE")) {
            return new Blue();
        }
        return null;
    }
}

class FactoryProducer {
    public static AbstractFactory getFactory(String choice) {
        if (choice.equalsIgnoreCase("SHAPE")) {
            return new ShapeFactory1();
        } else if (choice.equalsIgnoreCase("COLOR")) {
            return new ColorFactory();
        }
        return null;
    }
}

// Builder Pattern
class Item {
    private String name;
    private Packing packing;
    private float price;
    
    public String name() {
        return name;
    }
    
    public Packing packing() {
        return packing;
    }
    
    public float price() {
        return price;
    }
}

interface Packing {
    String pack();
}

class Wrapper implements Packing {
    @Override
    public String pack() {
        return "Wrapper";
    }
}

class Bottle implements Packing {
    @Override
    public String pack() {
        return "Bottle";
    }
}

abstract class Burger implements Item {
    @Override
    public Packing packing() {
        return new Wrapper();
    }
    
    @Override
    public abstract float price();
}

abstract class ColdDrink implements Item {
    @Override
    public Packing packing() {
        return new Bottle();
    }
    
    @Override
    public abstract float price();
}

class VegBurger extends Burger {
    @Override
    public float price() {
        return 25.0f;
    }
    
    @Override
    public String name() {
        return "Veg Burger";
    }
}

class ChickenBurger extends Burger {
    @Override
    public float price() {
        return 50.5f;
    }
    
    @Override
    public String name() {
        return "Chicken Burger";
    }
}

class Coke extends ColdDrink {
    @Override
    public float price() {
        return 30.0f;
    }
    
    @Override
    public String name() {
        return "Coke";
    }
}

class Pepsi extends ColdDrink {
    @Override
    public float price() {
        return 35.0f;
    }
    
    @Override
    public String name() {
        return "Pepsi";
    }
}

class Meal {
    private List<Item> items = new ArrayList<Item>();
    
    public void addItem(Item item) {
        items.add(item);
    }
    
    public float getCost() {
        float cost = 0.0f;
        for (Item item : items) {
            cost += item.price();
        }
        return cost;
    }
    
    public void showItems() {
        for (Item item : items) {
            System.out.print("Item : " + item.name());
            System.out.print(", Packing : " + item.packing().pack());
            System.out.println(", Price : " + item.price());
        }
    }
}

class MealBuilder {
    public Meal prepareVegMeal() {
        Meal meal = new Meal();
        meal.addItem(new VegBurger());
        meal.addItem(new Coke());
        return meal;
    }
    
    public Meal prepareNonVegMeal() {
        Meal meal = new Meal();
        meal.addItem(new ChickenBurger());
        meal.addItem(new Pepsi());
        return meal;
    }
}

// Prototype Pattern
abstract class Shape2 implements Cloneable {
    private String id;
    protected String type;
    
    abstract void draw();
    
    public String getType() {
        return type;
    }
    
    public String getId() {
        return id;
    }
    
    public void setId(String id) {
        this.id = id;
    }
    
    public Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
}

class Rectangle1 extends Shape2 {
    public Rectangle1() {
        type = "Rectangle";
    }
    
    @Override
    public void draw() {
        System.out.println("Inside Rectangle::draw() method.");
    }
}

class Square1 extends Shape2 {
    public Square1() {
        type = "Square";
    }
    
    @Override
    public void draw() {
        System.out.println("Inside Square::draw() method.");
    }
}

class Circle2 extends Shape2 {
    public Circle2() {
        type = "Circle";
    }
    
    @Override
    public void draw() {
        System.out.println("Inside Circle::draw() method.");
    }
}

class ShapeCache {
    private static Hashtable<String, Shape2> shapeMap = new Hashtable<String, Shape2>();
    
    public static Shape2 getShape(String shapeId) {
        Shape2 cachedShape = shapeMap.get(shapeId);
        return (Shape2) cachedShape.clone();
    }
    
    public static void loadCache() {
        Circle2 circle = new Circle2();
        circle.setId("1");
        shapeMap.put(circle.getId(), circle);
        
        Square1 square = new Square1();
        square.setId("2");
        shapeMap.put(square.getId(), square);
        
        Rectangle1 rectangle = new Rectangle1();
        rectangle.setId("3");
        shapeMap.put(rectangle.getId(), rectangle);
    }
}

// Structural Patterns Implementations

// Adapter Pattern
interface MediaPlayer {
    void play(String audioType, String fileName);
}

interface AdvancedMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
}

class VlcPlayer implements AdvancedMediaPlayer {
    @Override
    public void playVlc(String fileName) {
        System.out.println("Playing vlc file. Name: " + fileName);
    }
    
    @Override
    public void playMp4(String fileName) {
        // Do nothing
    }
}

class Mp4Player implements AdvancedMediaPlayer {
    @Override
    public void playVlc(String fileName) {
        // Do nothing
    }
    
    @Override
    public void playMp4(String fileName) {
        System.out.println("Playing mp4 file. Name: " + fileName);
    }
}

class MediaAdapter implements MediaPlayer {
    AdvancedMediaPlayer advancedMusicPlayer;
    
    public MediaAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer = new VlcPlayer();
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMusicPlayer = new Mp4Player();
        }
    }
    
    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer.playVlc(fileName);
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMusicPlayer.playMp4(fileName);
        }
    }
}

class AudioPlayer implements MediaPlayer {
    MediaAdapter mediaAdapter;
    
    @Override
    public void play(String audioType, String fileName) {
        // Inbuilt support to play mp3 music files
        if (audioType.equalsIgnoreCase("mp3")) {
            System.out.println("Playing mp3 file. Name: " + fileName);
        }
        // MediaAdapter is providing support to play other file formats
        else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        } else {
            System.out.println("Invalid media. " + audioType + " format not supported");
        }
    }
}

// Decorator Pattern
interface Shape3 {
    void draw();
}

class Rectangle2 implements Shape3 {
    @Override
    public void draw() {
        System.out.println("Shape: Rectangle");
    }
}

class Circle3 implements Shape3 {
    @Override
    public void draw() {
        System.out.println("Shape: Circle");
    }
}

abstract class ShapeDecorator implements Shape3 {
    protected Shape3 decoratedShape;
    
    public ShapeDecorator(Shape3 decoratedShape) {
        this.decoratedShape = decoratedShape;
    }
    
    public void draw() {
        decoratedShape.draw();
    }
}

class RedShapeDecorator extends ShapeDecorator {
    public RedShapeDecorator(Shape3 decoratedShape) {
        super(decoratedShape);
    }
    
    @Override
    public void draw() {
        decoratedShape.draw();
        setRedBorder(decoratedShape);
    }
    
    private void setRedBorder(Shape3 decoratedShape) {
        System.out.println("Border Color: Red");
    }
}

// Proxy Pattern
interface Image {
    void display();
}

class RealImage implements Image {
    private String fileName;
    
    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk(fileName);
    }
    
    @Override
    public void display() {
        System.out.println("Displaying " + fileName);
    }
    
    private void loadFromDisk(String fileName) {
        System.out.println("Loading " + fileName);
    }
}

class ProxyImage implements Image {
    private RealImage realImage;
    private String fileName;
    
    public ProxyImage(String fileName) {
        this.fileName = fileName;
    }
    
    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(fileName);
        }
        realImage.display();
    }
}

// Facade Pattern
class ShapeMaker {
    private Shape3 circle;
    private Shape3 rectangle;
    private Shape3 square;
    
    public ShapeMaker() {
        circle = new Circle3();
        rectangle = new Rectangle2();
        square = new Square1();
    }
    
    public void drawCircle() {
        circle.draw();
    }
    
    public void drawRectangle() {
        rectangle.draw();
    }
    
    public void drawSquare() {
        square.draw();
    }
}

// Bridge Pattern
interface DrawAPI {
    public void drawCircle(int radius, int x, int y);
}

class RedCircle implements DrawAPI {
    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("Drawing Circle[ color: red, radius: " + radius + ", x: " + x + ", " + y + "]");
    }
}

class GreenCircle implements DrawAPI {
    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("Drawing Circle[ color: green, radius: " + radius + ", x: " + x + ", " + y + "]");
    }
}

abstract class Shape1 {
    protected DrawAPI drawAPI;
    
    protected Shape1(DrawAPI drawAPI) {
        this.drawAPI = drawAPI;
    }
    
    public abstract void draw();
}

class Circle1 extends Shape1 {
    private int x, y, radius;
    
    public Circle1(int x, int y, int radius, DrawAPI drawAPI) {
        super(drawAPI);
        this.x = x;
        this.y = y;
        this.radius = radius;
    }
    
    public void draw() {
        drawAPI.drawCircle(radius, x, y);
    }
}

// Composite Pattern
class Employee {
    private String name;
    private String dept;
    private int salary;
    private List<Employee> subordinates;
    
    public Employee(String name, String dept, int sal) {
        this.name = name;
        this.dept = dept;
        this.salary = sal;
        subordinates = new ArrayList<Employee>();
    }
    
    public void add(Employee e) {
        subordinates.add(e);
    }
    
    public void remove(Employee e) {
        subordinates.remove(e);
    }
    
    public List<Employee> getSubordinates() {
        return subordinates;
    }
    
    public String toString() {
        return ("Employee :[ Name : " + name + ", dept : " + dept + ", salary :" + salary + " ]");
    }
}

// Flyweight Pattern
interface Shape4 {
    void draw();
}

class Circle4 implements Shape4 {
    private String color;
    private int x;
    private int y;
    private int radius;
    
    public Circle4(String color) {
        this.color = color;
    }
    
    public void setX(int x) {
        this.x = x;
    }
    
    public void setY(int y) {
        this.y = y;
    }
    
    public void setRadius(int radius) {
        this.radius = radius;
    }
    
    @Override
    public void draw() {
        System.out.println("Circle: Draw() [Color : " + color + ", x : " + x + ", y :" + y + ", radius :" + radius);
    }
}

class ShapeFactory2 {
    private static final HashMap<String, Shape4> circleMap = new HashMap<>();
    
    public static Shape4 getCircle(String color) {
        Circle4 circle = (Circle4) circleMap.get(color);
        
        if (circle == null) {
            circle = new Circle4(color);
            circleMap.put(color, circle);
            System.out.println("Creating circle of color : " + color);
        }
        return circle;
    }
}

// Behavioral Patterns Implementations

// Strategy Pattern
interface Strategy {
    public int doOperation(int num1, int num2);
}

class OperationAdd implements Strategy {
    @Override
    public int doOperation(int num1, int num2) {
        return num1 + num2;
    }
}

class OperationSubstract implements Strategy {
    @Override
    public int doOperation(int num1, int num2) {
        return num1 - num2;
    }
}

class OperationMultiply implements Strategy {
    @Override
    public int doOperation(int num1, int num2) {
        return num1 * num2;
    }
}

class Context {
    private Strategy strategy;
    
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
    
    public int executeStrategy(int num1, int num2) {
        return strategy.doOperation(num1, num2);
    }
}

// Observer Pattern
class Subject {
    private List<Observer> observers = new ArrayList<Observer>();
    private int state;
    
    public int getState() {
        return state;
    }
    
    public void setState(int state) {
        this.state = state;
        notifyAllObservers();
    }
    
    public void attach(Observer observer) {
        observers.add(observer);
    }
    
    public void notifyAllObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

abstract class Observer {
    protected Subject subject;
    public abstract void update();
}

class BinaryObserver extends Observer {
    public BinaryObserver(Subject subject) {
        this.subject = subject;
        this.subject.attach(this);
    }
    
    @Override
    public void update() {
        System.out.println("Binary String: " + Integer.toBinaryString(subject.getState()));
    }
}

class OctalObserver extends Observer {
    public OctalObserver(Subject subject) {
        this.subject = subject;
        this.subject.attach(this);
    }
    
    @Override
    public void update() {
        System.out.println("Octal String: " + Integer.toOctalString(subject.getState()));
    }
}

class HexaObserver extends Observer {
    public HexaObserver(Subject subject) {
        this.subject = subject;
        this.subject.attach(this);
    }
    
    @Override
    public void update() {
        System.out.println("Hex String: " + Integer.toHexString(subject.getState()).toUpperCase());
    }
}

// Command Pattern
class Stock {
    private String name = "ABC";
    private int quantity = 10;
    
    public void buy() {
        System.out.println("Stock [ Name: " + name + ", Quantity: " + quantity + " ] bought");
    }
    
    public void sell() {
        System.out.println("Stock [ Name: " + name + ", Quantity: " + quantity + " ] sold");
    }
}

interface Order {
    void execute();
}

class BuyStock implements Order {
    private Stock abcStock;
    
    public BuyStock(Stock abcStock) {
        this.abcStock = abcStock;
    }
    
    public void execute() {
        abcStock.buy();
    }
}

class SellStock implements Order {
    private Stock abcStock;
    
    public SellStock(Stock abcStock) {
        this.abcStock = abcStock;
    }
    
    public void execute() {
        abcStock.sell();
    }
}

class Broker {
    private List<Order> orderList = new ArrayList<Order>();
    
    public void takeOrder(Order order) {
        orderList.add(order);
    }
    
    public void placeOrders() {
        for (Order order : orderList) {
            order.execute();
        }
        orderList.clear();
    }
}

// Template Method Pattern
abstract class Game {
    abstract void initialize();
    abstract void startPlay();
    abstract void endPlay();
    
    // Template
    public final void play() {
        // Initialize the game
        initialize();
        
        // Start game
        startPlay();
        
        // End game
        endPlay();
    }
}

class Cricket extends Game {
    @Override
    void endPlay() {
        System.out.println("Cricket Game Finished!");
    }
    
    @Override
    void initialize() {
        System.out.println("Cricket Game Initialized! Start playing.");
    }
    
    @Override
    void startPlay() {
        System.out.println("Cricket Game Started. Enjoy the game!");
    }
}

class Football extends Game {
    @Override
    void endPlay() {
        System.out.println("Football Game Finished!");
    }
    
    @Override
    void initialize() {
        System.out.println("Football Game Initialized! Start playing.");
    }
    
    @Override
    void startPlay() {
        System.out.println("Football Game Started. Enjoy the game!");
    }
}

// Iterator Pattern
interface Iterator {
    public boolean hasNext();
    public Object next();
}

interface Container {
    public Iterator getIterator();
}

class NameRepository implements Container {
    public String names[] = {"Robert", "John", "Julie", "Lora"};
    
    @Override
    public Iterator getIterator() {
        return new NameIterator();
    }
    
    private class NameIterator implements Iterator {
        int index;
        
        @Override
        public boolean hasNext() {
            if (index < names.length) {
                return true;
            }
            return false;
        }
        
        @Override
        public Object next() {
            if (this.hasNext()) {
                return names[index++];
            }
            return null;
        }
    }
}

// Chain of Responsibility Pattern
abstract class AbstractLogger {
    public static int INFO = 1;
    public static int DEBUG = 2;
    public static int ERROR = 3;
    
    protected int level;
    
    // Next element in chain of responsibility
    protected AbstractLogger nextLogger;
    
    public void setNextLogger(AbstractLogger nextLogger) {
        this.nextLogger = nextLogger;
    }
    
    public void logMessage(int level, String message) {
        if (this.level <= level) {
            write(message);
        }
        if (nextLogger != null) {
            nextLogger.logMessage(level, message);
        }
    }
    
    abstract protected void write(String message);
}

class ConsoleLogger extends AbstractLogger {
    public ConsoleLogger(int level) {
        this.level = level;
    }
    
    @Override
    protected void write(String message) {
        System.out.println("Standard Console::Logger: " + message);
    }
}

class ErrorLogger extends AbstractLogger {
    public ErrorLogger(int level) {
        this.level = level;
    }
    
    @Override
    protected void write(String message) {
        System.out.println("Error Console::Logger: " + message);
    }
}

class FileLogger extends AbstractLogger {
    public FileLogger(int level) {
        this.level = level;
    }
    
    @Override
    protected void write(String message) {
        System.out.println("File::Logger: " + message);
    }
}

// State Pattern
class Context1 {
    private State state;
    
    public Context1() {
        state = null;
    }
    
    public void setState(State state) {
        this.state = state;
    }
    
    public State getState() {
        return state;
    }
}

interface State {
    public void doAction(Context1 context);
}

class StartState implements State {
    public void doAction(Context1 context) {
        System.out.println("Player is in start state");
        context.setState(this);
    }
    
    public String toString() {
        return "Start State";
    }
}

class StopState implements State {
    public void doAction(Context1 context) {
        System.out.println("Player is in stop state");
        context.setState(this);
    }
    
    public String toString() {
        return "Stop State";
    }
}

// Visitor Pattern
interface ComputerPart {
    public void accept(ComputerPartVisitor computerPartVisitor);
}

class Keyboard implements ComputerPart {
    @Override
    public void accept(ComputerPartVisitor computerPartVisitor) {
        computerPartVisitor.visit(this);
    }
}

class Monitor implements ComputerPart {
    @Override
    public void accept(ComputerPartVisitor computerPartVisitor) {
        computerPartVisitor.visit(this);
    }
}

class Mouse implements ComputerPart {
    @Override
    public void accept(ComputerPartVisitor computerPartVisitor) {
        computerPartVisitor.visit(this);
    }
}

class Computer implements ComputerPart {
    ComputerPart[] parts;
    
    public Computer() {
        parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};
    }
    
    @Override
    public void accept(ComputerPartVisitor computerPartVisitor) {
        for (int i = 0; i < parts.length; i++) {
            parts[i].accept(computerPartVisitor);
        }
        computerPartVisitor.visit(this);
    }
}

interface ComputerPartVisitor {
    public void visit(Computer computer);
    public void visit(Mouse mouse);
    public void visit(Keyboard keyboard);
    public void visit(Monitor monitor);
}

class ComputerPartDisplayVisitor implements ComputerPartVisitor {
    @Override
    public void visit(Computer computer) {
        System.out.println("Displaying Computer.");
    }
    
    @Override
    public void visit(Mouse mouse) {
        System.out.println("Displaying Mouse.");
    }
    
    @Override
    public void visit(Keyboard keyboard) {
        System.out.println("Displaying Keyboard.");
    }
    
    @Override
    public void visit(Monitor monitor) {
        System.out.println("Displaying Monitor.");
    }
}

// Mediator Pattern
class User {
    private String name;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public User(String name) {
        this.name = name;
    }
    
    public void sendMessage(String message) {
        ChatRoom.showMessage(this, message);
    }
}

class ChatRoom {
    public static void showMessage(User user, String message) {
        System.out.println(new Date().toString() + " [" + user.getName() + "] : " + message);
    }
}

// Memento Pattern
class Memento {
    private String state;
    
    public Memento(String state) {
        this.state = state;
    }
    
    public String getState() {
        return state;
    }
}

class Originator {
    private String state;
    
    public void setState(String state) {
        this.state = state;
    }
    
    public String getState() {
        return state;
    }
    
    public Memento saveStateToMemento() {
        return new Memento(state);
    }
    
    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
    }
}

class CareTaker {
    private List<Memento> mementoList = new ArrayList<Memento>();
    
    public void add(Memento state) {
        mementoList.add(state);
    }
    
    public Memento get(int index) {
        return mementoList.get(index);
    }
}

// Interpreter Pattern
interface Expression {
    public boolean interpret(String context);
}

class TerminalExpression implements Expression {
    private String data;
    
    public TerminalExpression(String data) {
        this.data = data;
    }
    
    @Override
    public boolean interpret(String context) {
        if (context.contains(data)) {
            return true;
        }
        return false;
    }
}

class OrExpression implements Expression {
    private Expression expr1 = null;
    private Expression expr2 = null;
    
    public OrExpression(Expression expr1, Expression expr2) {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }
    
    @Override
    public boolean interpret(String context) {
        return expr1.interpret(context) || expr2.interpret(context);
    }
}

class AndExpression implements Expression {
    private Expression expr1 = null;
    private Expression expr2 = null;
    
    public AndExpression(Expression expr1, Expression expr2) {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }
    
    @Override
    public boolean interpret(String context) {
        return expr1.interpret(context) && expr2.interpret(context);
    }
}